﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Economo.ExceptionHandling;

namespace Economo.Services.EntitiesConversion
{
    public static class Convertions
    {
        #region DTO 2 Entity
        /// <summary>
        /// Converts a DTO to BusinessEntity
        /// </summary>
        /// <typeparam name="T">Business Type</typeparam>
        /// <param name="sourceObj">DTO Object</param>        
        /// <returns>Returns the entity according to mapping defined on the entity metadata</returns>        
        public static T ConvertToBusinessEntity<T>(object sourceObj)
        {
            return ConvertToBusinessEntity<T>(sourceObj, false, null);
        }

        /// <summary>
        /// Converts a DTO to BusinessEntity
        /// </summary>
        /// <typeparam name="T">Business Type</typeparam>
        /// <param name="sourceObj">DTO Object</param>
        /// <param name="ignoreMissingProps">Ignore missing properties?</param>       
        /// <returns>Returns the entity according to mapping defined on the entity metadata</returns>        
        public static T ConvertToBusinessEntity<T>(object sourceObj, bool ignoreMissingProps)
        {
            return ConvertToBusinessEntity<T>(sourceObj, ignoreMissingProps, null);
        }

        /// <summary>
        /// Converts a DTO to BusinessEntity
        /// </summary>
        /// <typeparam name="T">Business Type</typeparam>
        /// <param name="sourceObj">DTO Object</param>
        /// <param name="ignoreMissingProps">Ignore missing properties?</param>
        /// <param name="formatter">Formatter to modify the returned value</param>
        /// <returns>Returns the entity according to mapping defined on the entity metadata</returns>        
        public static T ConvertToBusinessEntity<T>(object sourceObj, bool ignoreMissingProps, Formatter<T> formatter)
        {
            T result = default(T);

            if (sourceObj != null)
            {
                result = Activator.CreateInstance<T>();
                Type toType = typeof(T);
                

                foreach (var toProperty in toType.GetProperties())
                {
                        var attribute = Attribute.GetCustomAttribute(toProperty, typeof(MappingSettingsAttribute)) as MappingSettingsAttribute;

                    if (attribute != null && !string.IsNullOrEmpty(attribute.FieldName))
                    {
                        PropertyInfo sourceProp = sourceObj.GetType().GetProperty(attribute.FieldName);

                        if (sourceProp == null)
                        {
                            if (!ignoreMissingProps)
                                throw new  BusinessLogicException("Cannot convert " + sourceObj.GetType().ToString() + " to " + toType.ToString() + ". The property " + attribute.FieldName + " does not exist in type " + sourceObj.ToString());
                        }
                        else
                        {
                            toProperty.SetValue(result, sourceProp.GetValue(sourceObj, null), null);
                        }
                    }
                }

                if (result != null && formatter != null)
                {
                    result = formatter.Format(result);
                }
            }

            return result;
        }
        #endregion

        #region Entity 2 DTO
        /// <summary>
        /// Converts BusinessEntity to DTO 
        /// </summary>
        /// <typeparam name="T">DTO Type</typeparam>  
        /// <param name="sourceObj">Entity object</param>              
        /// <returns>Returns a DTO object mapping the properties according to the attributes of the entity</returns>
        public static T ConvertToDataEntity<T>(object sourceObj)
        {
            return ConvertToDataEntity<T>(sourceObj, false);
        }

        /// <summary>
        /// Converts BusinessEntity to DTO 
        /// </summary>
        /// <typeparam name="T">DTO Type</typeparam>  
        /// <param name="sourceObj">Entity object</param>
        /// <param name="ignoreMissingProps">Ignore missing properites?</param>        
        /// <returns>Returns a DTO object mapping the properties according to the attributes of the entity</returns>
        public static T ConvertToDataEntity<T>(object sourceObj, bool ignoreMissingProps)
        {
            T result = default(T);

            if (sourceObj != null)
            {
                result = Activator.CreateInstance<T>();

                Type sourceType = sourceObj.GetType();
                Type toType = typeof(T);

                foreach (var fromProperty in sourceType.GetProperties())
                {
                    //Se obtiene el atributo que contiene la información de mapeo
                    var attribute = Attribute.GetCustomAttribute(fromProperty, typeof(MappingSettingsAttribute)) as MappingSettingsAttribute;

                    if (attribute != null && !string.IsNullOrEmpty(attribute.FieldName))
                    {
                        PropertyInfo toProp = toType.GetProperty(attribute.FieldName);

                        if (toProp == null)
                        {
                            //No se pudo obtener la propiedad especificada en el mapping
                            if (!ignoreMissingProps)
                                throw new BusinessLogicException("Cannot convert " + sourceType.ToString() + " to " + toType.ToString() + ". The property " + attribute.FieldName + " does not exist in type " + toType.ToString());
                        }
                        else
                        {
                            object fromValue = fromProperty.GetValue(sourceObj, null);

                            if (fromProperty.PropertyType.IsEnum)
                            {
                                fromValue = (int)fromValue;
                            }

                            toProp.SetValue(result, fromValue, null);
                        }
                    }
                }
            }

            return result;
        }
        #endregion


        #region Lists convertions
        /// <summary>
        /// Converts a list of DTOs to a list of BusinessEntites
        /// </summary>
        /// <typeparam name="T">Entities type</typeparam>
        /// <typeparam name="Q">DTO type</typeparam>
        /// <param name="currentList">Instance of the DTO List</param>
        /// <returns></returns>
        public static List<T> ConvertToBusinessEntity<T, Q>(this List<Q> currentList)
        {
            List<T> result = new List<T>();


            foreach (Q element in currentList)
            {
                result.Add(Convertions.ConvertToBusinessEntity<T>(element));
            }

            return result;
        }

        /// <summary>
        /// Converts a list of DTOs to a list of BusinessEntites
        /// </summary>
        /// <typeparam name="T">Entities type</typeparam>
        /// <typeparam name="Q">DTO type</typeparam>
        /// <param name="currentList">Instance of the DTO List</param>
        /// <param name="formater">Object to format entity</param>
        /// <returns></returns>
        public static List<T> ConvertToBusinessEntity<T, Q>(this List<Q> currentList, Formatter<T> formater)
        {
            List<T> result = new List<T>();

            foreach (Q element in currentList)
            {
                result.Add(Convertions.ConvertToBusinessEntity<T>(element, false, formater));
            }

            return result;
        }

        /// <summary>
        /// Converts a Entities list to a list of DTOs
        /// </summary>
        /// <typeparam name="T">DTO Type</typeparam>
        /// <typeparam name="Q">Entity Type</typeparam>
        /// <param name="currentList">Instance of entities list</param>
        /// <returns></returns>
        public static List<T> ConvertToDataEntity<T, Q>(this List<Q> currentList)
        {
            List<T> result = new List<T>();

            foreach (Q element in currentList)
            {
                result.Add(Convertions.ConvertToDataEntity<T>(element));
            }

            return result;
        }
        #endregion
    }
}
